package com.example.demo.config.db.interceptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.example.demo.common.util.SM4Encryptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.ibatis.executor.Executor;

@Slf4j
@Component
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class QueryInterceptor implements Interceptor {

//    private static final Logger log = LoggerFactory.getLogger(QueryInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        Object[] args = invocation.getArgs();
        String mapperMethod = null;

        // 从MappedStatement参数中获取到操作类型
        if (args[0] instanceof MappedStatement) {
            MappedStatement ms = (MappedStatement) args[0];
            mapperMethod = ms.getId();
            log.debug("Encryption interceptor 执行方法： {}", mapperMethod);
        }
        log.debug("Encryption interceptor 操作参数：{}",args[1]);

        for (int i = 0; i < args.length; i++) {
            log.info("走到这里:{}",args[i]);
        }
        if (args[1] != null) {
            Class<?> aClass = args[1].getClass();
            String name = aClass.getName();
            log.info("name:{}",name);
            log.info("mapperMethod:{}", mapperMethod);
            // 只拦截selectByExample和分页列表的情况下的selectByExample_COUNT方法
            if ((StrUtil.endWith(mapperMethod, "selectList") || mapperMethod.contains("UserMapper"))) {
                // 通过参数对象xxxExample，获取实体类，进而获取哪些字段需要加密
                int lastIndex = mapperMethod.lastIndexOf('.');
                int secondLastIndex = mapperMethod.lastIndexOf('.', lastIndex - 1);
                String result = mapperMethod.substring(secondLastIndex + 1, lastIndex);
                String pojoName = result.replace("Mapper", "");
//                String pojoName = StrUtil.removeSuffix(name, "User");
                Class<?> pojoClass = Class.forName(pojoName);
                Object newInstance = pojoClass.newInstance();
                // 判断参数
                if (newInstance instanceof Encrypted) {
                    encryptParams((Encrypted)newInstance, args[1]);
                }
            }
        }

        Object result = invocation.proceed();
        if (result instanceof ArrayList) {
            @SuppressWarnings("rawtypes")
            ArrayList list = (ArrayList) result;
            if (list.size() == 0) {
                return result;
            }
            if (list.get(0) instanceof Encrypted) {
                for (Object item : list) {
                    decryptField((Encrypted) item);
                }
            }
            return result;
        }
        if (result instanceof Encrypted) {
            decryptField((Encrypted) result);
        }
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
    * 通过查询注解@Encrypt或者Encrypted返回的字段,进行解密 两种方法互斥
    * @author wultn
    * @date 9:41 2023/8/4
     **/
     private void decryptField(Encrypted object) throws IllegalAccessException, NoSuchFieldException {
        String[] encryptFields = object.getEncryptFields();
        Class<?> clazz = object.getClass();

        if (encryptFields.length == 0) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                // 含有Encrypt注解的字段需要解密处理
                 Encrypt encrypt = field.getAnnotation(Encrypt.class);
                if (encrypt != null && field.get(object) != null) {
                    String decryptString = null;
                    try {
                        decryptString = SM4Encryptor.decryptStr(field.get(object).toString());
                    } catch (Exception e) {
                        log.debug("Encryption interceptor，encrypt fail field : {}, value: {}", field.getName(), field.get(object).toString());
                    }
                    if (decryptString != null) {
                        field.set(object, decryptString);
                        log.debug("Encryption interceptor，encrypt field: {}", field.getName());
                    }
                }
            }
        } else {
            for (String fieldName : encryptFields) {
                Field field;
                try {
                    field = clazz.getDeclaredField(fieldName);
                } catch (Exception e) {
                    // 有可能是继承
                    field = clazz.getSuperclass().getDeclaredField(fieldName);
                }
                field.setAccessible(true);
                if (field.get(object) != null) {
                    String decryptString = null;
                    try {
                        decryptString = SM4Encryptor.decryptStr(field.get(object).toString());
                    } catch (Exception e) {
                        log.debug("Encryption interceptor，encrypt fail field : {}, value: {}", field.getName(), field.get(object).toString());
                    }
                    if (decryptString != null && decryptString.length() > 0) {
                        field.set(object, decryptString);
                        log.debug("Encryption interceptor，encrypt field: {}", field.getName());
                    }
                }
            }
        }
    }

    /**
    * 通过查询注解@Encrypt或者Encrypted返回的字段,进行动态加密 两种方法互斥
    * @author wultn
    * @date 9:41 2023/8/4
     **/
      private void encryptParams(Encrypted object, Object example) throws IllegalAccessException, NoSuchFieldException {
        log.info("加密实体：{}", object);
          String[] encryptFields = object.getEncryptFields();
        Class<?> clazz = object.getClass();
        // 查询参数对象 xxxExample
        Class<?> exampleClazz = example.getClass();
        Field oredCriteriaField = exampleClazz.getDeclaredField("oredCriteria");
        oredCriteriaField.setAccessible(true);
        // oredCriteria 一般情况下只有一个元素，使用example.or()的情况下会有多个
        List<Object> oredCriteriaList= ((List<Object>)oredCriteriaField.get(example));
        if (CollUtil.isNotEmpty(oredCriteriaList)) {
            for (Object criteriaObject : oredCriteriaList) {
                if (criteriaObject != null) {
                    Field criteriaField = criteriaObject.getClass().getSuperclass().getDeclaredField("criteria");
                    criteriaField.setAccessible(true);
                    List<Object> criteriaList = (List<Object>)criteriaField.get(criteriaObject);
                    if (CollUtil.isNotEmpty(criteriaList)) {
                        for (Object criteria : criteriaList) {
                            Field condition = criteria.getClass().getDeclaredField("condition");
                            condition.setAccessible(true);
                            if (encryptFields.length == 0) {
                                Field[] fields = clazz.getDeclaredFields();
                                for (Field field : fields) {
                                    field.setAccessible(true);
                                    Encrypt encrypt = field.getAnnotation(Encrypt.class);
                                    if(encrypt != null && field.get(object) != null) {
                                        String encryptString = SM4Encryptor.encryptStr(field.get(object).toString());
                                        field.set(object, encryptString);
                                        log.debug("Encryption interceptor，encrypt field: {}", field.getName());
                                    }
                                    if (encrypt != null && covertStr(condition.get(criteria).toString()).equals(field.getName().toUpperCase() + "=")) {
                                        // 加密查询参数
                                        Field valueField = criteria.getClass().getDeclaredField("value");
                                        valueField.setAccessible(true);
                                        Object value = valueField.get(criteria);
                                        if (value != null) {
                                            valueField.set(criteria, SM4Encryptor.encryptStr(value.toString()));
                                            log.debug("Encryption interceptor，encrypt params: {}", value);
                                        }
                                    }
                                }
                            } else {
                                for (String fieldName : encryptFields) {
                                    // 加密查询参数
                                    if (covertStr(condition.get(criteria).toString()).equals(fieldName.toUpperCase() + "=")) {
                                        Field valueField = criteria.getClass().getDeclaredField("value");
                                        valueField.setAccessible(true);
                                        Object value = valueField.get(criteria);
                                        if (value != null) {
                                            valueField.set(criteria, SM4Encryptor.encryptStr(value.toString()));
                                            log.debug("Encryption interceptor，encrypt params: {}", value);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
    * 输入： member_name = ,输出MEMBERNAME=
    * @author wultn
    * @date 15:58 2023/8/7
    **/
    private String covertStr (String value) {
        return value.replace(" ", "").replace("_", "").toUpperCase();
    }
}